﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics.Filter;

namespace LowLevelGraphics.Extractors
{
    /// <summary>
    /// Use this class object to restore lost information
    /// 
    /// To do this this needs two pics as an input:
    /// 
    /// - Source
    /// - Mask
    /// 
    /// The source is the pic containing the information loss.
    /// 
    /// The mask is a graylevel or monochrome pic containing the regions that have to be restored.
    /// White regions mark regions that have to be restored. Black regions are regions that are considered to be ok.
    /// If the mask is a graylevel pic (not monochrome) the graylevel will represent the transparency.
    /// 
    /// You may use a filter to created the mask. To do this use any filter like this:
    /// 
    /// Bitmap maskBitmap =  Filter.ExecuteCloned(sourceBitmap)
    /// Impainter.Restore(sourceBitmap, maskBitmap)
    /// 
    /// Attention: maskBitmap and sourceBitmap must have the same size
    /// </summary>

    public enum IMPAINTER_MODE
    {
        /// <summary>
        /// Mask is created by the algorithm itself, only one iteration
        /// </summary>
        FULLY_AUTOMATIC_1_ITERATION, //

        /// <summary>
        /// Mask is created by the algorithm itself, and recreated until only black pixels are left in mask
        /// </summary>
        FULLY_AUTOMATIC_UNTIL_MASK_BLACKED_OUT,

        /// <summary>
        ///manual mode (specify iterations yourself)        /// </summary>
        MANUALLY,

        /// <summary>
        /// This is the best mode, 
        /// this considers the isophote lines (German: Isophotenlinien)
        /// </summary>
        ISOPHOTE_LINES_BERTALMIO
    }

    /// <summary>
    /// 
    /// </summary>
    public class Impainter
    {
        private int m_nIterations = 1;
        private bool m_bMarkMaskInSource = false;
        private IMPAINTER_MODE M_IMPAINTER_MODE = IMPAINTER_MODE.MANUALLY;

        /// <summary>
        /// Initializes a new instance of the <see cref="Impainter"/> class.
        /// </summary>
        public Impainter()
            : this(1, IMPAINTER_MODE.MANUALLY)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Impainter"/> class.
        /// </summary>
        /// <param name="_MODE">The _ MODE.</param>
        public Impainter(IMPAINTER_MODE _MODE)
            : this(1, _MODE)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Impainter"/> class.
        /// </summary>
        /// <param name="_nIterations">The _n iterations.</param>
        public Impainter(int _nIterations)
            : this(_nIterations, IMPAINTER_MODE.MANUALLY)
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Impainter"/> class.
        /// </summary>
        /// <param name="_nIterations">The _n iterations.</param>
        /// <param name="_MODE">The _ MODE.</param>
        public Impainter(int _nIterations, IMPAINTER_MODE _MODE)
        {
            m_nIterations = _nIterations;
            M_IMPAINTER_MODE = _MODE;
        }

        /// <summary>
        /// Tries to create a mask bitmap on its own
        /// </summary>
        /// <param name="_bitmapSource"></param>
        public void Restore(UnsafeBitmap _bitmapSource)
        {
            UnsafeBitmap bitmapMask = (UnsafeBitmap)_bitmapSource.Clone();
            new Contrast(90).Execute(bitmapMask);
            new BarcodeFilter(180,false).Execute(bitmapMask);
            Restore(_bitmapSource, bitmapMask);
        }

        /// <summary>
        /// Executes the retouching
        /// </summary>
        public void Restore(UnsafeBitmap _bitmapSource, UnsafeBitmap _bitmapMask)
        {
            switch (M_IMPAINTER_MODE)
            {
                //TODO
                //1.) Bevor der Inpainting-Algorithmus über das Bild geht, wird eine anisotropische
                //    Diffusionsglättung gemacht, d.h. Störungen werden beseitigt. Vor allem
                //    mit dem Ziel, dass die Isophotenlinien, die @­ berühren, nicht davon beeinflusst
                //    werden.

                //Formel
                //@I@t(x; y; t) = g²(x; y) ¤ ·(x; y; t) ¤ jrI(x; y; t)j; 8(x; y) 2 ­²

                //­² ist eine Vergrößerung von ­ mit einem Kreis mit Radius ².
                //· ist euklidische Krümmung der Isophoten.
                //g²(x; y) =
                //(
                //1 , falls (x; y) 2 ­²
                //0 , falls (x; y) 2 @­²

                //2.)
                //Nach mehreren Schritten des iterativen Inpainting folgt anisotr. Diffusion,
                //damit die Schärfe im rekonstruierten Bereich nicht verloren geht und die
                //Kanten- die quer durch die Maske gehen- verbunden werden, so dass sie ununterbrochen
                //weiterlaufen. Das nächste Beispiel zeigt wie wichtig es ist das
                //Bild zu glätten, bzw. wie häufig
                //s. ImageInpainting-Ausarbeitung
                case IMPAINTER_MODE.ISOPHOTE_LINES_BERTALMIO:

                    break;
            }

            UnsafeBitmap bitmapSourceCloned = (UnsafeBitmap)_bitmapSource.Clone();
            UnsafeBitmap bitmapSource = _bitmapSource;
            UnsafeBitmap bitmapMask = _bitmapMask;
            int nHeightMask = bitmapMask.Height;
            int nWidthMask = bitmapMask.Width;
            if (m_bMarkMaskInSource)
            {
                for (int y = 0; y < nHeightMask; y++)
                {
                    for (int x = 0; x < nWidthMask; x++)
                    {
                        Color color = bitmapMask.GetPixel(x, y);
                        if (color != Definitions.Black)
                        {
                            color = bitmapSource.GetPixel(x,y );
                            bitmapSource.SetPixel(x, y, Color.Red);
                        }
                    }
                }
                bitmapSource.Dispose();
                bitmapMask.Dispose();
                return;
            }

            for (int i = 0; i < m_nIterations; i++)
            {
                if (M_IMPAINTER_MODE == IMPAINTER_MODE.FULLY_AUTOMATIC_UNTIL_MASK_BLACKED_OUT)
                {
                    bitmapSource.Dispose();
                    bitmapMask.Dispose();

                    _bitmapMask = (Bitmap)_bitmapSource.Clone();
                    new Contrast(90).Execute(_bitmapMask);
                    new BarcodeFilter(180, false).Execute(_bitmapMask);
                    bitmapMask = new UnsafeBitmap(_bitmapMask);
                    bitmapSource = new UnsafeBitmap(_bitmapSource);
                }
                for (int y = 0; y < nHeightMask; y++)
                {
                    for (int x = 0; x < nWidthMask; x++)
                    {
                        Color color = bitmapMask.GetPixel(x, y);
                        if (color != Definitions.Black)
                        {
                            int nX = 0;
                            int nY = 0;
                            foreach (Point point in Definitions.EnvironmentPointListClockwise)
                            {
                                nX = x + point.X;
                                nY = y + point.Y;
                                if (nX < 0 || nY < 0 || nX > nWidthMask - 1 || nY > nHeightMask - 1)
                                {
                                    continue;
                                }
                                if (bitmapMask.GetPixel(nX, nY) != Definitions.Black)
                                {
                                    color = bitmapSourceCloned.GetPixel(nX, nX);
                                    bitmapSource.SetPixel(x, y, color);
                                }
                                break;
                            }
                        }
                    }
                }
            }

            bitmapMask.Dispose();
            bitmapSource.Dispose();
        }
    }
}
